#ifndef _FNT_COMMAND_H_
#define _FNT_COMMAND_H_

#include <QString>
#include "graph.h"

struct Position;
struct SessionPriv;
class ListModel;
class LabelListModel;

class Command {
	SessionPriv* _sp;
	const QString _title;
	Position _curPos, _curPosNew;
	Position _tgtPos, _tgtPosNew;
	bool _performed;
	bool _curChg, _tgtChg;

	protected:
	Command(SessionPriv* tp, const QString& t);
	void setCurPos(const Position& pos) { _curPosNew=pos; _curChg=true; }
	void setTgtPos(const Position& pos) { _tgtPosNew=pos; _tgtChg=true; }
	const Position& curPos() const { return _curChg?_curPosNew:_curPos; }
	const Position& tgtPos() const { return _tgtChg?_tgtPosNew:_tgtPos; }
	void mapEdgeMultiple(Edge e, std::vector<std::tuple<Edge, size_t, bool>> dats);
	void mapEdge(Edge e, Edge ne, size_t i, bool dir);
	void updateBuffer(EdgePriv* ep);
	void updateBuffer(EdgePriv* ep,Point leftPoint,Point rightPoint);
	void updateBuffer(EdgePriv* ep, size_t i);
	void freeBuffer(EdgePriv* ep);
	virtual void undoImp(SessionPriv* tp) =0;
	virtual void redoImp(SessionPriv* tp) =0;
	bool performed() const { return _performed; }
	Graph graph() const;
	ListModel* model() const;
	LabelListModel* labelModel() const;
	bool isTargetPosOnPath() const;

	public:
	const QString& title() const { return _title; }
	void undo(SessionPriv* tp);
	void redo(SessionPriv* tp);
	virtual ~Command() { }

	static Command* renameNeuron(SessionPriv* tp, Tree n, const std::string& name);
	static Command* changeType(SessionPriv* tp, Edge e, int16_t t);
	static Command* changeMark(SessionPriv* tp, Edge e, size_t ep, int16_t m);
	static Command* changeSize(SessionPriv* tp, Edge e, size_t ep, uint16_t m);
	static Command* createNeuron(SessionPriv* tp, Vertex v);
	static Command* removeNeurons(SessionPriv* tp, const std::vector<size_t>& idxes);
	static Command* purgeNeurons(SessionPriv* tp, const std::vector<size_t>& idxes);
	static Command* finishBranch(SessionPriv* tp, Vertex v, Edge e, size_t ei);
	static Command* checkBranch(SessionPriv* tp, Vertex v, Edge e, size_t ei,size_t ei_prev);
	static Command* createBranch(SessionPriv* tp, const Path& path);
	static Command* extendBranch(SessionPriv* tp, const Path& path);
	static Command* removeEdge(SessionPriv* tp, Edge e0, size_t ei0, Edge e1, size_t ei1);
	static Command* importFnt(SessionPriv* tp, const std::vector<bool>& vertInfo, const std::vector<std::tuple<size_t, size_t, int16_t, std::vector<Point>>>& edgInfo, const std::vector<std::tuple<size_t, std::string>>& somaInfo);
	static Command* importSwc(SessionPriv* tp, const std::vector<Point>& points, const std::vector<int64_t>& parents);
};

#endif
